#include "my_disk_membufs.h"

// return the first node less than or equal to offset
static my_rbtree_node_t *
my_disk_membufs_rbtree_frist_small(my_rbtree_node_t *node, my_rbtree_node_t *sentinel,
    int64_t offset) {

    my_rbtree_node_t *save = NULL;
    while(node != sentinel) {
        if (node->key == offset) {
            return node;
        }

        if (node->key > offset) {
            node = node->left;
            continue;
        }

        save = node;
        node = node->right;
    }

    return save;
}

// return the first node greater than or equal to offset
static my_rbtree_node_t *
my_disk_membufs_rbtree_first_largest(my_rbtree_node_t *node, my_rbtree_node_t *sentinel,
    int64_t offset) {

    my_rbtree_node_t *save = NULL;
    while(node != sentinel) {
        if (node->key == offset) {
            return node;
        }

        if (node->key < offset) {
            node = node->right;
            continue;
        }

        save = node;
        node = node->left;
    }

    return save;
}

my_disk_buf_t *
my_disk_membufs_get_helper(my_disk_membufs_t *membufs, int64_t offset,
    int64_t size) {

    // find a buffer taht meets the following conditions.
    // - disk_buf->start_off <= offset && offset + size <= disk_buf->start_off + MY_DISK_BLOCK_SIZE
    my_rbtree_node_t *node = my_disk_membufs_rbtree_frist_small(membufs->rbtree.root,
        membufs->rbtree.sentinel, offset);
    if (!node) {
        return NULL;
    }

    my_disk_buf_t *buf = (my_disk_buf_t *)((char *)node - offsetof(my_disk_buf_t, tree_node));
    if (offset + size > buf->start_off + MY_DISK_BLOCK_SIZE) {
        return  NULL;
    }

    return buf;
}


my_disk_buf_t * // object offset and object size
my_disk_membufs_get(my_disk_membufs_t *membufs, int64_t offset, int64_t size) {
    my_disk_buf_t *buf;

    my_rwlock_rlock(&membufs->rwlock);
    buf = my_disk_membufs_get_helper(membufs, offset, size);
    my_rwlock_runlock(&membufs->rwlock);

    return buf;
}

void
my_disk_membufs_put(my_disk_membufs_t *membufs, my_disk_buf_t *buf) {
    my_rwlock_wlock(&membufs->rwlock);
    buf->tree_node.key = buf->start_off;
    my_rbtree_insert(&membufs->rbtree, &buf->tree_node);
    my_rwlock_wunlock(&membufs->rwlock);
}

void
my_disk_membufs_del(my_disk_membufs_t *membufs, my_disk_buf_t *buf) {
    my_rwlock_wlock(&membufs->rwlock);
    my_rbtree_delete(&membufs->rbtree, &buf->tree_node);
    my_rwlock_wunlock(&membufs->rwlock);
}